import datetime
import logging
import os

from config import envConfig, config
from instructions.instruction import Instruction
from instructions.enum_types import InstructionType, ActionType, ExcelType
from tools import over_deadline, concat_file_reply
from wechat.reply import ReplyType, Reply
import pandas as pd

logger = logging.getLogger(__name__)


class StaticInstruction(Instruction):
    """
    This class of Instruction is used to handle the verification queries, it's no need to query the AI model.
    Only provide the database information. Normally, it returns a dict at handler_query function.
    In this case, query argument will be the instrucment type and other arguments are sent by kwargs
    """

    def __init__(self, chatDBHandler=None, query=None, **kwargs):
        super().__init__(chatDBHandler=chatDBHandler, query=query)
        self.insType = query
        self.query = query
        self.kwargs = kwargs
        logger.info(f"[instruction] Init Instruction {self.insType}")

    async def handle_query(self) -> Reply:
        if self.insType == InstructionType.CHECK_USER_ALREADY_EXISTS:
            self.chatDBHandler.add_actions(ActionType.CHECK_USER_EXISTS)
            user = self.kwargs["user"]
            rawRes = {
                "【名字】": user.name,
                "【微信昵称】": user.nickname,
                "【手机号】": user.phone,
                "【地址】": user.address,
                "【提示】": envConfig.WX_REGISTERED_HELP
            }
            return Reply(ReplyType.TEXT, rawRes)
        elif self.insType == InstructionType.DELETE_USER:
            try:
                user = self.kwargs["user"]
                await user.delete()
                self.chatDBHandler.add_actions(ActionType.DELETE_SUCCESS)
                rawRes = {
                    "【名字】": user.name,
                    "【微信昵称】": user.nickname,
                    "【手机号】": user.phone,
                    "【地址】": user.address,
                    "【提示】": envConfig.WX_DELETE_HELP
                }
                return Reply(ReplyType.TEXT, rawRes)
            except Exception as e:
                self.chatDBHandler.add_actions(ActionType.DELETE_FAILED)
                logger.error(f"[instruction]Error occurred when deleting user: {e}")
                return Reply(ReplyType.TEXT, "对不起，删除用户失败了，要不待会再试试？或者再问问我爹？[委屈]")
        elif self.insType == InstructionType.CHECKSUM_FILE:
            """
            WX已经检测过文件存在以及允许的类型，这里只要直接读取文件，对比表头.
            CHECKSUM规则：
            1. 首先通过时间过滤，过了每天的 PACKAGE_DEADLINE_TIME 就不再处理文件导入
            2. 检查文件表头是否和配置中既定的表头匹配上
            3. 如果是金桔表格，则提醒用户需要私聊, 如果是订单表格，则检查时间
            """
            excel = pd.read_excel(self.kwargs["fpath"])
            ftype = self.check_file_type(excel.columns)
            if ftype:
                if ftype == ExcelType.KUMQUAT and self.chatDBHandler.msg.is_group:
                    os.remove(self.kwargs["fpath"])
                    return Reply(ReplyType.TEXT, "金桔表格属于敏感信息，请私下重新发给我噢~[嘿哈]，我嘴很严！")
                if ftype == ExcelType.ORDER:
                    self.chatDBHandler.add_actions(ActionType.CHECK_DEADLINE)
                    delta = over_deadline()
                    if delta.days < 0 and config.PACKAGE_DEADLINE_ON:  # 过时间了，删除文件，不处理
                        self.chatDBHandler.add_actions(ActionType.OVER_DEADLINE)
                        logger.warning("[deadline check] Order loaded is over time. Skip!")
                        os.remove(self.kwargs["fpath"])
                        return Reply(ReplyType.TEXT, StaticInstruction.generate_over_deadline_msg(delta))
                self.chatDBHandler.add_actions(ActionType.CHECKSUM_KUMQUAT_PASS) if ftype == "金桔表格" \
                    else ActionType.CHECKSUM_ORDER_PASS
                logger.info("[checksum] 表头检查过关。")
                filename = os.path.basename(self.kwargs['fpath'])
                return Reply(ReplyType.TEXT,
                             f"你的【{filename}】已经保存了哟[呲牙]~如果需要导入请【引用这份文件】，说【金桔王 导入文件】即可。")
            else:
                self.chatDBHandler.add_actions(ActionType.CHECKSUM_FAILED)
                os.remove(self.kwargs["fpath"])
                logger.warning("[checksum] 文件表头检查不过关，跳过。")
                return Reply(ReplyType.SKIP, "")
        elif self.insType == InstructionType.GET_TEMPLATE_KEYS:
            found_tpl_str = "订单模板文件在这里哟[呲牙]~"
            if self.kwargs["tpl"] == "模板":
                return Reply(ReplyType.TEXT, "你要哪个模板呢？[疑问]说具体的模板文件啊~[撇嘴]，是要【订单模板】还是【金桔模板】？")
            elif "订单模板" in self.kwargs["tpl"]:
                self.desire_rtype = "file"
                return Reply(ReplyType.FILE, concat_file_reply(config.ORDER_TEMPLATE_FILE, found_tpl_str))
            else:
                self.desire_rtype = "file"
                return Reply(ReplyType.FILE, concat_file_reply(config.ORDER_TEMPLATE_FILE, found_tpl_str))
        else:
            return Reply(ReplyType.TEXT, "Unknown instruction or Not implemented")

    @staticmethod
    def check_file_type(columns) -> ExcelType | None:
        """
        Check the file type if it's order or kumquat
        :param columns: dataframe's header
        :return:
        """
        if set(config.ORDER_HEADERS.values()).issubset(columns):
            # self.chatDBHandler.add_actions(ActionType.CHECKSUM_ORDER_PASS)
            return ExcelType.ORDER
        elif set(config.KUMQUATS_HEADERS.values()).issubset(columns):
            # self.chatDBHandler.add_actions(ActionType.CHECKSUM_ORDER_PASS)
            return ExcelType.KUMQUAT

    @staticmethod
    def generate_over_deadline_msg(delta: datetime.timedelta, need_curse=False) -> str:
        """
        Generate interesting response according to over timer.
        :param need_curse: 是否咒骂对方
        :param delta:
        :return:
        """
        hours = delta.days * 24 + delta.seconds/3600
        over_hours = int(hours*-1)
        if need_curse:
            if over_hours < 1:
                return "[天啊]刚过订单的截至时间没多久，但我是公事公办，不会给你开后门。除非..除非你给我爹发个[红包]...[旺柴]"
            elif 1 <= over_hours <= 4:
                return f"今天都已经在装果了你才发来，来不及了[捂脸] 明天记得{config.PACKAGE_DEADLINE_TIME.strftime('%H:%S')}之前给我。"
            else:
                return f"离截至时间都过了{int(over_hours)}个小时了，黄花菜都凉了[咒骂]，生米都煮成熟饭了[咒骂]！" \
                       f"算了，明天记得{config.PACKAGE_DEADLINE_TIME.strftime('%H:%S')}之前给我。"
        else:
            return f"已经过了{int(over_hours)}小时，过了截止时间啦。这个文件我不处理哟~"
